package pathfinder

import (
	"encoding/json"
	"math"
	"os"
	"path/filepath"
	"src/zeus/linmath"
	"src/zeus/logger"
	log "src/zeus/logger"
	"strconv"
	"strings"
)

// -------------------以下是加载客户端导出的寻路路点的功能--------------------------//
var MapNavPoints map[string]*NavPoints = make(map[string]*NavPoints)

func InitNavPoints() bool {
	err := filepath.Walk("../res/pathfinder/navpoints", func(path string, f os.FileInfo, err error) error {
		if f == nil {
			return nil
		}
		if f.IsDir() {
			return nil
		}
		if strings.HasSuffix(path, ".json") {
			data, err := os.ReadFile(path)
			if err != nil {
				log.Error("ReadFile NavPoints err: ", err)
			}
			var Points NavPoints
			err = json.Unmarshal(data, &Points)
			if err != nil {
				log.Error("Unmarshal NavPoints err: ", err)
			} else {
				_, ok := MapNavPoints[f.Name()]
				if ok {
					log.Debug("NavPoints has same filename ", f.Name())
				} else {
					MapNavPoints[f.Name()] = &Points
				}
			}

			log.Debug("NavPoints path ", path)
		}

		return nil
	})

	if err != nil {
		log.Error("walk pathpoints file error")
		return false
	}
	return true
}

func GetNavPoints(key string) (*NavPoints, bool) {
	value, ok := MapNavPoints[key]
	if ok {
		return value, true
	}
	return nil, false
}

type NavPointData struct {
	Points []linmath.Vector3
}

type NavPoints struct {
	NavMap map[string]*NavPointData
}

//-------------------以下是客户端导出寻路路点使用的功能--------------------------//

func NewNavPointsFinder(filename string, navgationfinder IPathFinder) *NavPointsFinder {
	navpoints, ok := GetNavPoints(filename + ".json")
	if !ok {
		return nil
	}
	ipathfinder := &NavPointsFinder{
		navpoints:       navpoints,
		navgationFinder: navgationfinder,
	}
	return ipathfinder
}

type NavPointsFinder struct {
	navpoints       *NavPoints
	navgationFinder IPathFinder
}

func (this *NavPointsFinder) FindPath(params interface{}) ([]linmath.Vector3, bool) {
	navpoint_params, ok := params.(NavPointParams)
	if !ok {
		log.Error("NavPointsFinder params not match")
		return nil, false
	}
	s_e := strconv.Itoa(int(navpoint_params.sgroupid)) + "_" + strconv.Itoa(int(navpoint_params.egroupid))
	navpointdata, ok := this.navpoints.NavMap[s_e]
	if !ok || len(navpointdata.Points) == 0 {
		return this.navgationFinder.FindPath(navpoint_params.start, navpoint_params.end)
	}
	sid, eid := this.findNearPoint(navpoint_params.start, navpoint_params.end, navpointdata.Points)
	var retpoints []linmath.Vector3 = make([]linmath.Vector3, 0, 16)
	spoint := navpointdata.Points[sid]
	if !spoint.IsEqualXYZ(navpoint_params.start) {
		points, ok := this.navgationFinder.FindPath(navpoint_params.start, spoint)
		if !ok {
			return nil, false
		}
		retpoints = append(retpoints, points...)
	} else {
		retpoints = append(retpoints, spoint)
	}
	if eid-sid > 1 {
		middle := navpointdata.Points[sid+1 : eid]
		retpoints = append(retpoints, middle...)
	}
	epoint := navpointdata.Points[eid]
	if !epoint.IsEqualXYZ(navpoint_params.end) {
		points, ok := this.navgationFinder.FindPath(epoint, navpoint_params.end)
		if !ok {
			return nil, false
		}
		retpoints = append(retpoints, points...)
	} else {
		retpoints = append(retpoints, epoint)
	}
	return retpoints, true
}

// GetNear 获取某个点附近的合法位置
// 参数：
//
//	params 检查点 *linmath.Vector3 类型
//
// 返回值：
//
//	pos 路点，注意第一个点就是输入的起始点
//	result 是否能成功
func (this *NavPointsFinder) GetNear(srcPos *linmath.Vector3) (*linmath.Vector3, bool) {
	return nil, false
}

func (this *NavPointsFinder) CheckWall(srcPos, dstPos linmath.Vector3) (path []linmath.Vector3, bHaveWall bool) {
	bHaveWall = false
	return
}

func (this *NavPointsFinder) findNearPoint(start linmath.Vector3, end linmath.Vector3, points []linmath.Vector3) (int, int) {
	var sid int
	var eid int
	var slen float32 = math.MaxFloat32
	var elen float32 = math.MaxFloat32
	for i, value := range points {
		sdis := value.Sub(start).Len()
		edis := value.Sub(end).Len()
		if sdis < slen {
			sid = i
			slen = sdis
		}
		if edis < elen {
			eid = i
			elen = edis
		}
	}
	return sid, eid
}

// Raycast 射线检测
func (this *NavPointsFinder) Raycast(params interface{}) (*linmath.Vector3, bool) {
	return nil, false
}

func (this *NavPointsFinder) RaycastEx(srcPos, dstPos linmath.Vector3) (*linmath.Vector3, bool) {
	return nil, false
}

func (this *NavPointsFinder) CreateObstacleCylinder(doorName string, centPos linmath.Vector3, radius, height int32) {
}

func (this *NavPointsFinder) CreateObstacleOBBBox(doorName string, centPos, halfPos linmath.Vector3, YawDegree int32) {
}

func (this *NavPointsFinder) RemoveObstacle(doorName string) {

}

// 清理全部的门
func (this *NavPointsFinder) RemoveAllObstacle() {

}

func (this *NavPointsFinder) UpdateObstacle() {

}

func (this *NavPointsFinder) Destroy() {

}

func (this *NavPointsFinder) Loop() {

}

func (this *NavPointsFinder) MeshCheckRushPath(srcPos, stpos linmath.Vector3) linmath.Vector3 {
	if tmpPos, ok := this.RaycastEx(srcPos, stpos); !ok {
		logger.Debug(logger.LogKey_MoveSys, " [FindCollection] MeshCheckRushPath 未能检测到墙直接通过 ", stpos)
		return stpos
	} else {
		if tmpPos.IsEqualXZ(srcPos) {
			if path, ok := this.FindPath(PackageFindParams(srcPos, stpos)); ok {
				if len(path) == 2 {
					*tmpPos = path[1]
					logger.Debug(logger.LogKey_MoveSys, " [FindCollection] MeshCheckRushPath 检测到墙 再次尝试走到墙根 ", path[1])
				}
			}
		} else {
			logger.Debug(logger.LogKey_MoveSys, " [FindCollection] MeshCheckRushPath 检测到墙 ", tmpPos)
		}
		stpos = *tmpPos
		return stpos
	}
}
